<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>手游开发杂谈</title>
    <style>
      @import url(https://fonts.googleapis.com/css?family=Ubuntu);
      @import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz);
      @import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700,400italic);

      body {
        font-family: 'Ubuntu';
      }
      h1, h2, h3 {
        font-family: 'Yanone Kaffeesatz';
        font-weight: 400;
        margin-bottom: 0;
      }
      .remark-slide-content h1 { font-size: 3.2em; }
      .remark-slide-content h2 { font-size: 2.4em; }
      .remark-slide-content h3 { font-size: 1.6em; }
      .footnote {
        position: absolute;
        bottom: 1em;
      }

      .linknode {
        position: absolute;
        bottom: 1em;
        left: 18em;
        font-size: 0.68em;
      }

      li { line-height: 1.8em; }
      li p { line-height: 1.25em; }
      .red { color: #fa0000; }
      .large { font-size: 2em; }
      a, a > code {
        /*color: rgb(249, 38, 114);*/
        color: rgb(68, 168, 255);
        text-decoration: none;
      }
      code {
        background: #e7e8e2;
        border-radius: 5px;
      }
      .remark-code, .remark-inline-code { 
        font-family: 'Ubuntu Mono';
        font-size: 0.85em;
        line-height: 1.2em;
      }
      .remark-code-line-highlighted     { background-color: #373832; }
      .pull-left {
        float: left;
        width: 47%;
      }
      .pull-right {
        float: right;
        width: 47%;
      }
      .pull-right ~ p {
        clear: both;
      }
      #slideshow .slide .content code {
        font-size: 0.8em;
      }
      #slideshow .slide .content pre code {
        font-size: 0.9em;
        padding: 15px;
      }
      .inverse {
        background: #272822;
        color: #777872;
        text-shadow: 0 0 20px #333;
      }
      .inverse h1, .inverse h2 {
        color: #f3f3f3;
        line-height: 0.8em;
      }

      /* Slide-specific styling */
      #slide-inverse .footnote {
        bottom: 12px;
        left: 20px;
      }
      #slide-how .slides {
        font-size: 0.9em;
        position: absolute;
        top:  151px;
        right: 140px;
      }
      #slide-how .slides h3 {
        margin-top: 0.2em;
      }
      #slide-how .slides .first, #slide-how .slides .second {
        padding: 1px 20px;
        height: 90px;
        width: 120px;
        -moz-box-shadow: 0 0 10px #777;
        -webkit-box-shadow: 0 0 10px #777;
        box-shadow: 0 0 10px #777;
      }
      #slide-how .slides .first {
        background: #fff;
        position: absolute;
        top: 20%;
        left: 20%;
        z-index: 1;
      }
      #slide-how .slides .second {
        position: relative;
        background: #fff;
        z-index: 0;
      }

      /* Two-column layout */
      .left-column {
        color: #777;
        width: 20%;
        height: 92%;
        float: left;
      }
      .left-column h2:last-of-type, .left-column h3:last-child {
        color: #000;
      }
      .right-column {
        width: 75%;
        float: right;
        padding-top: 1em;
      }

    </style>
  </head>
  <body>
    <textarea id="source">
name: inverse
layout: true
class: center, middle, inverse

---

# Something interesting

[手游开发杂谈]

.footnote[Go directly to [project site](https://gitee.com/codesprite/Sharing)]

---

## Life is short. You need Python.
人生苦短，我用Python

.footnote[Go directly to [project site](https://gitee.com/codesprite/Sharing)]

---

layout: false

## 为何如此推荐Python

.left-column[

### 高级

]

.right-column[
  
### 用最短的时间解决最需要解决的问题

* 比如，完成同一个任务，C语言要写1000行代码，Java只需要写100行，而Python可能只要20行

* 代码少的代价是运行速度慢，C程序运行1秒钟，Java程序可能需要2秒，而Python程序可能就需要10秒

* 所以，python适用于对性能不敏感，需要通过自动化来解放劳动力或需要快速开发的场合

* 比如自动化日常任务，可以做网站，很多著名的网站包括YouTube，豆瓣等就是Python写的

* 甚至很多在线游戏的后台都是Python开发的

]

---

## 为何如此推荐Python

.left-column[

### 高级
### 跨平台

]

.right-column[
  
### 跨平台，无需手动编译，解释型

* 可访问系统Api及其它语言编写的模块

* 系统命令行能做的事情，几乎都可以用python自动化执行

* 可以用C++等编写对性能要求高的模块，在python中调用

]

---

## 为何如此推荐Python

.left-column[

### 高级
### 跨平台
### 强大的库

]

.right-column[

### 非常强大的标准库和庞大的第三方库

* Python标准库很强大，能够帮助你完成许多工作，包括正则表达式、文档生成、单元测试、线程、数据库、网页浏览器、CGI、FTP、电子邮件、XML、XML-RPC、HTML、WAV、文件、加密、GUI以及其它系统相关的代码

* 记住，只要安装了Python，所有这些都能做到。这叫做Python的“遥控器”哲学

* 除了标准库，还有丰富的，高质量的第三方库，可以在Python包索引找到它们

]

.linknode[

* [Awesome Python List](https://github.com/jobbole/awesome-python-cn)

]

---

## 为何如此推荐Python

.left-column[

### 高级
### 跨平台
### 强大的库
### 简单专注

]

.right-column[

### 其哲学就是：“优雅”、“明确”、“简单”

* 基于以上几点，再加上python本身语法简约（像伪代码一样），让我更专注于解决问题本身，

* 阅读好的Python程序感觉就像阅读英语，尽管是非常严格的英语。

]

---

template: inverse

## 简单灵活，要啥有啥.

.footnote[Go directly to [project site](https://gitee.com/codesprite/Sharing)]

---

template: inverse

## Case 1: Batch modify excel files
案例1: 批量修改Excel文件

.footnote[Go directly to [project site](https://gitee.com/codesprite/Sharing)]

---

## Python Librarys

.left-column[

### gooey

]

.right-column[

Turn (almost) any Python command line program into a full GUI application with one line.

- 只加一行代码，将命令行工具变成GUI工具
- 根据ArgumentParser的参数类型自动匹配UI控件
- 跨平台输出一致，不受平台编码影响
- GUI界面支持多语言
- 更灵活，更强大的定制能力

```python
from gooey import Gooey

@Gooey(program_name='GUI工具')
def main():
    parse = ArgumentParser(description='GUI工具')
    parse.add_argument('--verbose', action='store_true', help='...')
```

]

.linknode[

* [Gooey](https://github.com/chriskiehl/Gooey)
* [ArgumentParser](https://docs.python.org/2/library/argparse.html)

]

---

## Python Librarys

.left-column[

### gooey
### logging

]

.right-column[

A simple, powerful python logging library.

- CRITICAL, ERROR, WARNING, INFO, DEBUG
- 多重输出并独立设置输出等级、格式、过滤等

```python
logger = logging.getLogger('Logger Tag')
logger.setLevel(logging.DEBUG)
# file handler
fh = logging.FileHandler('/path/to/logFile')
fh.setLevel(logging.WARNING)
fh.setFormatter(logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
logger.addHandler(fh)
# console handler
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(logging.Formatter('%(message)s'))
logger.addHandler(ch)
# then use:
logger.info('info..') # logger.debug('...') balabala
```

]

---

## Python Librarys

.left-column[

### gooey
### logging
### xlutils

]

.right-column[

- xlrd. 负责excel读取并操作数据
- xlwd. 负责将数据写入到excel文件
- xlutils. 基于xlrd和xlwt的一系列excel操作工具。

```python
# formatting_info=True 用于加载格式信息
workBook = xlrd.open_workbook(filePath, formatting_info=True)
sheet = workBook.sheets()[i] # 获取工作表数组
sheet.name # 工作表名称
sheet.ncols # 列数
sheet.nrows # 行数
cell = sheet.cell(row, col) # 获取一个单元格
cell.ctype # 单元格值类型
cell.value # 单元格值
cell.xf_index # 单元格样式索引
# 改变单元格的值
sheet.put_cell(row, col, ctype, value, xf_index)
# 保存修改后的表格，xlrd只负责读，所以需要通过xlutils来保存
xlutils.save(workBook, filePath)
```

]

.linknode[

* [xlutils](http://xlutils.readthedocs.io/en/latest/)
* [xlrd.sheet.Cell](http://xlrd.readthedocs.io/en/latest/api.html#xlrd.sheet.Cell)

]

---

template: inverse

## 手游优化简述

.footnote[Go directly to [project site](https://gitee.com/codesprite/Sharing)]

---

## 手游优化简述

手游优化其实是一个找最佳平衡点的问题。要平衡：开发维护工作量、优化深度、性能、画面表现等之间的关系。

主要评估几个方面：

* 帧速（性能优化最重要的指标，能达到60FPS是最好的，最小也得在45FPS，优化方式有很多，需要具体问题具体分析）
* 内存峰值（最好不要超过200M）
* 包体积（越小越好嘛）
* 加载速度（主要是减少玩家无聊的等待时间，一般是优化加载策略）
* 画面表现（优化一般会损失一定的画面质量，就看如何来平衡面面质量与优化深度的关系了）

.linknode[

* [详情移步](https://gitee.com/codesprite/Sharing/blob/master/手游优化简述.md)

]

---

template: inverse

## Case 2: Batch optimize png files
案例2: 批量优化压缩PNG文件

.footnote[Go directly to [project site](https://gitee.com/codesprite/Sharing)]

---

template: inverse

## 动态合图

.footnote[Go directly to [project site](https://gitee.com/codesprite/Sharing)]

---

.left-column[

### 优点 

]

.right-column[

动态合图的好处：

* GL_CALL过多问题
* 加载合图纹理时的内存峰值问题
* 合图纹理使用索引压缩后质量下降问题
* 为很多使用场景提供方便的合图策略（换主题，换地貌等）

]

---

.left-column[

### 优点 
### 二维装箱

]

.right-column[

二维装箱问题（rectangle packing/rectangle packer）目前仍没有最优解解决方案，而且对最优解的趋近和速度成反比。目前MaxRects应该是最好的算法了。

[UnityWiki](http://wiki.unity3d.com/index.php?title=MaxRectsBinPack)上面有Unity官方的代码。

关于MaxRects算法，[TexturePacker](https://www.codeandweb.com/texturepacker/documentation)的官方说明是：

* Best-known algorithm for packing textures
* Is fast and has a high packing ratio
* Enable rotation and trimming for best results

]

---

.left-column[

### 优点 
### 二维装箱
### 局部更新

]

.right-column[

### 纹理局部更新

```cpp
Texture2D::updateWithData(const void *data,
                          int offsetX,
                          int offsetY,
                          int width,
                          int height)
```

但有一点麻烦的地方，此方法使用的`glTexSubImage2D`要求图像宽度必须是2的倍数。

]

---

.left-column[

### 优点 
### 二维装箱
### 局部更新
### SpriteSheet

]

.right-column[

```cpp
/// 生成SpriteFrame
static SpriteFrame* createWithTexture(Texture2D* pobTexture, 
                                      const Rect& rect, 
                                      bool rotated, 
                                      const Vec2& offset, 
                                      const Size& originalSize);

/// 移除旧的SpriteFrame
SpriteFrameCache::getInstance()->removeSpriteFrameByName(imageName);

/// 添加新的SpriteFrame
SpriteFrameCache::getInstance()->addSpriteFrame(spriteFrame, imageName);

```

]

---

template: inverse

## 字体精简

.footnote[Go directly to [project site](https://gitee.com/codesprite/Sharing)]

---

### 字体精简

需求来源：曾经有个项目，画风独特，使用了漂亮的字体，还需要支持14国语言。经过漫长的选择最终确定用Google思源字体作为游戏基本字库（字符全并且无版权问题）。但字体体积非常大，超过10M。游戏里面集成一个这样的字体就够大了，更何况还想多集成几个好看的字体。于是想到对字体进行精简，去掉游戏里面没有用到的字符。由于这个游戏所用到的字符相对固定（也就是说没有用户可以随便输入文字的地方），所以精简下来的字体文件应该非常小。

网上找到的工具和解决方案：

* [font-spider](http://font-spider.org) 官网非常气派，一看就高大上，就是解决不了空格的问题。
* [fontmin](http://ecomfe.github.io/fontmin/) 百度出品，font-spider也是基于它的，空格的问题也是源于它。
* [sfntly](https://github.com/googlei18n/sfntly) Google出品，需要自己编译，Google上用的比较多。
* [FontZip](https://github.com/forJrking/FontZip) 一个Java工具，基于sfntly，简单有效，**推荐使用**。（界面略丑）

.linknode[

* [一个免费商用中文字体的索引，非常全，非常棒](http://zenozeng.github.io/Free-Chinese-Fonts/)
* [百度出的web版字体编辑器](http://font.baidu.com/editor/)，[github](https://github.com/ecomfe/fonteditor)

]

---

template: inverse

## SSHFS

.footnote[Go directly to [project site](https://gitee.com/codesprite/Sharing)]

---

## SSHFS

通过SSH连接，通过SFTP传输文件，不需要额外的配置，简单实用，效率高。安全性能很好。速度快，比samba强，无需配置，映射任何目录

文件系统基于FUSE，mac需要先安装osxfuse

FUSE：用户空间文件系统（Filesystem in Userspace，簡稱FUSE）是一个面向类Unix计算机操作系统的软件接口，它使无特权的用户能够无需编辑内核代码而创建自己的文件系统。目前Linux通过内核模块对此进行支持。

```
sshfs [user@]host:[dir] mountpoint [options]
```

然后就可以像操作本地文件一样操作远程目录啦！

---

template: inverse

## SSH打洞

.footnote[Go directly to [project site](https://gitee.com/codesprite/Sharing)]

---

### SSH打洞

利用SSH反向隧道穿透NAT，实现内网穿透。核心技术是端口转发，它能够将其他TCP端口的网络数据通过SSH链接来转发，并且自动提供了相应的加密及解密服务。这一过程有时也被叫做“隧道”（tunneling）。总的来说SSH端口转发能够提供两大功能：

1. 加密SSH Client端至SSH Server端之间的通讯数据。(保护隐私数据不被监听窃取)
2. 突破防火墙的限制完成一些之前无法建立的TCP连接。(将内网服务暴露到外网)

---

| 机器代号 | 机器位置 | 地址 | 账户 | ssh/sshd 端口 | 是否需要运行sshd |
| :--- | :--- | :--- | :--- | :--- | :--- |
| A | 公网 | xx.xx.xx.xx | usera | 22 | 是 |
| B | NAT之后 | 127.0.0.1 | userb | 22 | 是 |
| C | NAT之后 | 127.0.0.1 | userc | 22 | 否 |


目标：在C上通过SSH连接B

在B机器上执行: 

```
ssh -p <B ssh port> -qngfNTR <A port for B>:127.0.0.1:<B ssh port> <A user>@<A IP>
```
    
连接自己的SSH端口，转发到远程端口并绑定。

在A机器上执行: 

```
ssh -p <A port for B> <B user>@127.0.0.1
```

如果要用C机器连接B，需要先在A机器上将/etc/ssh/sshd_config文件中的GatewayPorts设置为yes，然后到C机器执行: 

```
ssh -p <A port for B> <B user>@<A IP>
```

---

name: last-page
template: inverse

## That's all folks (for now)!

![qrcode](qrcode.svg)

Slideshow created using [remark](http://github.com/gnab/remark).

    </textarea>
    <script src="https://remarkjs.com/downloads/remark-latest.min.js"></script>
    <script>
      var slideshow = remark.create({
          highlightStyle: 'monokai',
          highlightLanguage: 'remark',
          highlightLines: true,

          navigation: {
            scroll: false,
            touch: true,
            click: false
          }
        }) ;
    </script>
  </body>
</html>

